/*
 * © 2024 huayunliufeng保留所有权利, 依据MIT许可证发布。
 * 请勿更改或删除版权声明或此文件头。
 * 此代码是免费软件, 您可以重新分发和/或修改它。
 * 开源是希望它有用, 但不对代码做任何保证。
 * 如有疑问请联系: huayunliufeng@163.com
 */

package io.github.huayunliufeng.dbinfo.dbinfo;

import io.github.huayunliufeng.common.utils.HylfDataUtil;
import io.github.huayunliufeng.common.utils.HylfFunUtil;
import io.github.huayunliufeng.dbinfo.model.ProcedureColumnsInfo;
import io.github.huayunliufeng.dbinfo.model.ProceduresInfo;

import java.sql.Connection;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;

/**
 * <p>检索给定目录中可用的存储过程的描述。</p>
 * <p>只返回与模式和过程名条件匹配的过程描述。它们按PROCEDURE_CAT、PROCEDURE_SCHEMA、PROCEDURE_NAME和SPECIFIC_ NAME排序。</p>
 * <p>用户可能没有执行getProcedures返回的任何过程的权限</p>
 *
 * @author zhongq
 * @datetime 2024/4/1 10:55
 */
public class ObtainProceduresInfo extends ObtainDbInfo {
    private static Map<Connection, ObtainProceduresInfo> obtainProceduresInfoMap = null;

    private Map<String, List<ProceduresInfo>> proceduresInfoMap = null;
    private Map<String, List<ProcedureColumnsInfo>> procedureColumnsInfoMap = null;

    protected ObtainProceduresInfo(Connection conn) {
        super(conn);
    }

    public static ObtainProceduresInfo build(Connection conn) {
        obtainProceduresInfoMap = HylfFunUtil.mapAddValue(obtainProceduresInfoMap, conn, () -> new ObtainProceduresInfo(conn));
        return obtainProceduresInfoMap.get(conn);
    }

    /**
     * <p>检索给定目录中可用的存储过程的描述。</p>
     * <p>只返回与模式和过程名条件匹配的过程描述。它们按PROCEDURE_CAT、PROCEDURE_SCHEMA、PROCEDURE_NAME和SPECIFIC_ NAME排序。</p>
     * <p>用户可能没有执行getProcedures返回的任何过程的权限</p>
     *
     * @param catalog              目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schemaPattern        模式名称模式;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param procedureNamePattern 过程名模式;必须匹配存储在数据库中的过程名
     * @return 每一行都是一个过程描述
     */
    public List<ProceduresInfo> getProcedures(String catalog, String schemaPattern, String procedureNamePattern) {
        String logFormat = "获取数据库存储过程信息失败。[catalog = {}, schemaPattern = {}, procedureNamePattern = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", catalog, schemaPattern, procedureNamePattern);
            proceduresInfoMap = HylfFunUtil.mapAddValue(proceduresInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getProcedures(catalog, schemaPattern, procedureNamePattern)
                        , logFormat, catalog, schemaPattern, procedureNamePattern);
                List<ProceduresInfo> proceduresInfoList = HylfDataUtil.autoSetValue(resultSet, ProceduresInfo.class);
                for (ProceduresInfo proceduresInfo : proceduresInfoList) {
                    String procedureCat = proceduresInfo.getProcedureCat();
                    String procedureSchem = proceduresInfo.getProcedureSchem();
                    String procedureName = proceduresInfo.getProcedureName();
                    proceduresInfo.setProcedureColumnsInfoList(getProcedureColumns(procedureCat, procedureSchem, procedureName, "%"));
                }
                HylfFunUtil.autoClose(resultSet);
                return proceduresInfoList;
            });
            return proceduresInfoMap.get(key);
        });
    }

    /**
     * <p>检索给定目录中可用的存储过程的描述。</p>
     * <p>只返回与模式和过程名条件匹配的过程描述。它们按PROCEDURE_CAT、PROCEDURE_SCHEMA、PROCEDURE_NAME和SPECIFIC_ NAME排序。</p>
     * <p>用户可能没有执行getProcedures返回的任何过程的权限</p>
     *
     * @param procedureNamePattern 过程名模式;必须匹配存储在数据库中的过程名
     * @return 每一行都是一个过程描述
     */
    public List<ProceduresInfo> getProcedures(String procedureNamePattern) {
        return getProcedures(getCatalog(), getSchema(), procedureNamePattern);
    }

    /**
     * <p>检索给定目录的存储过程参数和结果列的描述。</p>
     * <p>只返回与模式、过程和参数名条件匹配的描述。它们按PROCEDURE_CAT、procedure_schema、PROCEDURE_NAME和SPECIFIC_NAME排序。</p>
     * <p>其中，返回值(如果有的话)是第一个。接下来是按调用顺序的参数描述。列描述按照列号顺序进行。</p>
     * <p>某些数据库可能不返回过程的列描述。</p>
     *
     * @param catalog              目录名称;必须与存储在数据库中的目录名称匹配;“”检索那些没有目录的;null表示不应使用目录名称来缩小搜索范围
     * @param schemaPattern        模式名称模式;必须与存储在数据库中的模式名称匹配;""检索那些没有模式的;null表示不应该使用模式名称来缩小搜索范围
     * @param procedureNamePattern 过程名模式;必须匹配存储在数据库中的过程名
     * @param columnNamePattern    列名模式;必须匹配存储在数据库中的列名
     * @return 每行描述一个存储过程参数或列
     */
    public List<ProcedureColumnsInfo> getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) {
        String logFormat = "获取数据库存储过程参数和结果列信息失败。[catalog = {}, schemaPattern = {}, procedureNamePattern = {}, columnNamePattern = {}]";
        return HylfFunUtil.methodVoidReturnExec(() -> {
            String key = String.join("-", catalog, schemaPattern, procedureNamePattern, columnNamePattern);
            procedureColumnsInfoMap = HylfFunUtil.mapAddValue(procedureColumnsInfoMap, key, () -> {
                ResultSet resultSet = HylfFunUtil.methodVoidReturnExec(
                        () -> databaseMetaData.getProcedureColumns(catalog, schemaPattern, procedureNamePattern, columnNamePattern)
                        , logFormat, catalog, schemaPattern, procedureNamePattern, columnNamePattern);
                List<ProcedureColumnsInfo> proceduresInfoList = HylfDataUtil.autoSetValue(resultSet, ProcedureColumnsInfo.class);
                HylfFunUtil.autoClose(resultSet);
                return proceduresInfoList;
            });
            return procedureColumnsInfoMap.get(key);
        });
    }

    /**
     * <p>检索给定目录的存储过程参数和结果列的描述。</p>
     * <p>只返回与模式、过程和参数名条件匹配的描述。它们按PROCEDURE_CAT、procedure_schema、PROCEDURE_NAME和SPECIFIC_NAME排序。</p>
     * <p>其中，返回值(如果有的话)是第一个。接下来是按调用顺序的参数描述。列描述按照列号顺序进行。</p>
     * <p>某些数据库可能不返回过程的列描述。</p>
     *
     * @param procedureNamePattern 过程名模式;必须匹配存储在数据库中的过程名
     * @param columnNamePattern    列名模式;必须匹配存储在数据库中的列名
     * @return 每行描述一个存储过程参数或列
     */
    public List<ProcedureColumnsInfo> getProcedureColumns(String procedureNamePattern, String columnNamePattern) {
        return getProcedureColumns(getCatalog(), getSchema(), procedureNamePattern, columnNamePattern);
    }
}
